package com.googlecode.lightest.core

/**
 * Responsible for actually performing tasks.
 */
class TaskDispatcher implements ITaskDispatcher {
    IPreferences prefs
    ITestEnvironment env
    ITaskDispatchStrategy strategy
    ITaskListener listener
    LightestTestCase testcase
    
    void setPreferences(IPreferences prefs) {
        this.prefs = prefs
    }
    
    void setEnvironment(ITestEnvironment env) {
        this.env = env
    }
    
    /**
     * @param strategy
     */
    void setStrategy(ITaskDispatchStrategy strategy) {
        this.strategy = strategy
    }
    
    void setListener(ITaskListener listener) {
        this.listener = listener
    }
    
    /**
     * Assigns a testcase to this dispatcher.
     *
     * @param testcase
     */
    void setTestCase(LightestTestCase testcase) {
        this.testcase = testcase
    }
    
    IPreferences getPreferences() {
        return prefs
    }
    
    ITestEnvironment getEnvironment() {
        return env
    }
    
    ITaskListener getListener() {
        return listener
    }
    
    /**
     * Returns the testcase this dispatcher is currently assigned to, or null
     * if it is not assigned to a testcase.
     */
    LightestTestCase getTestCase() {
        return testcase
    }
    
    /**
     * Dispatches the task and all nested tasks represented by the Node tree,
     * as available in the API of the current testcase, and using the current
     * dispatch strategy. The currently attached listener will be notified of
     * the task result. "parentResult" will be null for root tasks. Returns the
     * result of dispatching the task.
     *
     * @param taskNode      
     * @param parentResult
     */
    ITaskResult dispatch(TaskNode taskNode, ITaskResult parentResult = null) {
        def taskName = taskNode.name()
        def task = testcase.getApi().getTask(taskName)
        
        if (task == null) {
            def args = [ taskNode.attributes(), taskNode.value() ]
            throw new MissingMethodException(taskName, this.class, args)
        }
        
        task.configure(taskNode, prefs, env, parentResult)
        
        def taskResult = strategy.dispatch(task)
        
        if (taskResult.getStatus() == ITaskResult.STATUS_DOOMED) {
            throw new TaskDoomedException(taskResult.getMessage())
        }
        
        listener.onTaskComplete(taskResult)
        
        if (taskResult.getStatus() != ITaskResult.STATUS_FAILED) {
            for (childTaskNode in taskNode.children()) {
                dispatch(childTaskNode, taskResult)
            }
        }
        
        return taskResult
    }

}
